package com.dy.分类.二叉树.最近的公共祖先;
/*
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为：“对于有根树 T 的两个结点 p、q，
最近公共祖先表示为一个结点 x，满足 x 是 p、q 的祖先且 x 的深度尽可能大（一个节点也可以是它自己的祖先）。”

例如，给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4]

示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。


示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。


说明:

所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉树中。

 */
/**
 * 思路：
 * 两次前序遍历得到两个路径序列，再寻找两个序列的最后相同部分
 */

import com.dy.树.TreeNode;

import java.util.LinkedList;

public class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        LinkedList<TreeNode> Ppath = new LinkedList<>();
        LinkedList<TreeNode> Pres = new LinkedList<>();
        boolean Pfinish[] = new boolean[1];
        LinkedList<TreeNode> Qpath = new LinkedList<>();
        LinkedList<TreeNode> Qres = new LinkedList<>();
        boolean Qfinish[] = new boolean[1];

        preOrder(root, p, Ppath, Pres, Pfinish);
        preOrder(root, q, Qpath, Qres, Qfinish);
        int minSize = Math.min(Pres.size(), Qres.size());
        TreeNode res = null;
        for (int i = 0; i < minSize; i++) {
            if (Pres.get(i) == Qres.get(i)) {
                res = Pres.get(i);
            } else {
                break;
            }
        }
        return res;
    }

    public void preOrder(TreeNode root, TreeNode search, LinkedList<TreeNode> path,
                         LinkedList<TreeNode> res, boolean finish[]) {
        //当已经找到或者为空时直接返回
        if (root == null || finish[0] == true) {
            return;
        }
        path.add(root);
        if (root == search) {
            finish[0] = true;
            res.addAll(path);
        }
        preOrder(root.left, search, path, res, finish);
        preOrder(root.right, search, path, res, finish);
        //回退
        path.removeLast();
    }
/*
思路：从根节点开始遍历，如果node1和node2中的任一个和root匹配，那么root就是最低公共祖先。
 如果都不匹配，则分别递归左、右子树，如果有一个 节点出现在左子树，并且另一个节点出现在右子树，则root就是最低公共祖先.
 如果两个节点都出现在左子树，则说明最低公共祖先在左子树中，否则在右子树。
 */
    public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || p == root || q == root) {
            return root;
        }
        TreeNode left = lowestCommonAncestor2(root.left, p, q);
        TreeNode right = lowestCommonAncestor2(root.right, p, q);
        if (left != null && right != null) {
            return root;
        }
        else if (left == null) {
            return right;
        }
        else  {
            return left;
        }
    }

    public static void main(String[] args) {
        TreeNode root = new TreeNode(3);
        root.left = new TreeNode(5);
        root.right = new TreeNode(1);
        root.left.left = new TreeNode(6);
        root.left.right = new TreeNode(2);
        root.left.right.left = new TreeNode(7);
        root.left.right.right = new TreeNode(4);
        root.right.left = new TreeNode(0);
        root.right.right = new TreeNode(8);
        Solution solution = new Solution();
        solution.lowestCommonAncestor2(root, root.left.right.left,root.left.right.right );
    }
}
